﻿using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows;
using C1.Silverlight.Pdf;
using MyFloridaHouse.libSvDTO;
using System.Windows.Media;


namespace MyFloridaHouse.libSvOpenDistricts.PDF
{

    public class PDFdoc : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        public const double dotsPerInch = 72.0;

        internal C1PdfDocument pdf;
        PDFPoint pdfCursor = new PDFPoint();

        public double docWidth { get; set; }
        public double docHeight { get; set; }
        public double docFooterHeight { get; set; }
        public double docFooterWidth { get; set; }
        public double docFooterMargin { get; set; }

        public double usablePageWidth { get; set; }
        public double usablePageHeight { get; set; }
        public double usablePageMargin { get; set; }

        private double _docMargin = 1.0;
        public double docMargin
        {
            get { return _docMargin; }
            set
            {
                if (value != _docMargin)
                {
                    _docMargin = value;
                    usablePageWidth = docWidth - 2 * docMargin;
                    usablePageHeight = docHeight - 2 * docMargin;
                }
            }
        }

        public PDFdoc(enumDistrictPlanPDFPrintSizes pageSize)
        {
            double pageWidthInInches;
            double pageHeightInInches;

            if (enumDistrictPlanPDFPrintSizes._32x32 == pageSize)
            {
                pageWidthInInches = 32.0;
                pageHeightInInches = 32.0;
            }
            else
            {
                pageWidthInInches = 11.0;
                pageHeightInInches = 8.5;
            }
            pdf = new C1PdfDocument(PaperKind.Custom);
            pdf.PageSize = new Size(pageWidthInInches * dotsPerInch, pageHeightInInches * dotsPerInch);
            pdf.NumberDecimalDigits = 2;
            docWidth = pdf.PageRectangle.Width;
            docHeight = pdf.PageRectangle.Height;
            docMargin = 0.0;

        }

        public void Save(Stream stream)
        {
            pdf.Save(stream);
        }

        public static int ConvertZoomLevelToRoundingDecimalPoints(double zoomLevel)
        {
            // todo: ConvertZoomLevelToRoundingDecimalPoints: Beef up this algorithm
            int roundDecimalPoints = 1;
            if (zoomLevel > 7)
            {
                roundDecimalPoints = 6;
            }
            return roundDecimalPoints;
        }


        internal void DrawPolygonWithFill(List<Point> points, Pen borderPen, Pen fillPen, BoundingBoxDTO boundingBoxLatLon, Rect borderRectXY, double zoomLevel)
        {
            Point[] XYPoints = ConvertLatLonToXYList(points, boundingBoxLatLon, borderRectXY, ConvertZoomLevelToRoundingDecimalPoints(zoomLevel));
            pdf.SetClipRect(borderRectXY);
            pdf.FillPolygon(fillPen.Color, XYPoints);
            pdf.DrawPolygon(borderPen, XYPoints);
            pdf.ResetClipRect();
        }

        internal void DrawPolygon(List<Point> points, Pen borderPen, Pen fillPen, BoundingBoxDTO boundingBoxLatLon, Rect borderRectXY, double zoomLevel)
        {
            Point[] XYPoints = ConvertLatLonToXYList(points, boundingBoxLatLon, borderRectXY, ConvertZoomLevelToRoundingDecimalPoints(zoomLevel));
            pdf.SetClipRect(borderRectXY);
            pdf.DrawPolygon(borderPen, XYPoints);
            pdf.ResetClipRect();
        }


        internal void DrawPolyline(List<Point> points, Pen borderPen, Pen borderHighlightPen, BoundingBoxDTO boundingBoxLatLon, Rect borderRectXY, double zoomLevel)
        {
            Point[] XYPoints = ConvertLatLonToXYList(points, boundingBoxLatLon, borderRectXY, ConvertZoomLevelToRoundingDecimalPoints(zoomLevel));
            pdf.SetClipRect(borderRectXY);
            if (borderHighlightPen.Color != Colors.Transparent)
            {
                pdf.DrawLines(borderHighlightPen, XYPoints);
            }
            pdf.DrawLines(borderPen, XYPoints);
            pdf.ResetClipRect();
        }

        internal bool IsLatLonShapeInClipRect(BoundingBoxDTO shapeBoundingBox, BoundingBoxDTO latLonInView, Rect clipRect, double zoomLevel)
        {
            PDFPoint northwest = ConvertPointToXY(latLonInView, clipRect, shapeBoundingBox.northWest.longitude, shapeBoundingBox.northWest.latitude, zoomLevel);
            PDFPoint southEast = ConvertPointToXY(latLonInView, clipRect, shapeBoundingBox.southEast.longitude, shapeBoundingBox.southEast.latitude, zoomLevel);

            // if any outside boundary conditions are true, the shape is outside the clip
            if (   (southEast.x < clipRect.Left)
                 | (northwest.x > clipRect.Right)
                 | (southEast.y < clipRect.Top)
                 | (northwest.y > clipRect.Bottom)
                )
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        internal static PDFPoint ConvertPointToXY(BoundingBoxDTO mapBoundingBox, Rect mapInsertDrawingRect, double lngX, double latY, double zoomLevel)
        {
            List<Point> singlePointList = new List<Point>();
            singlePointList.Add(new Point(lngX, latY));
            Point[] XYPoints = ConvertLatLonToXYList(singlePointList, mapBoundingBox, mapInsertDrawingRect, ConvertZoomLevelToRoundingDecimalPoints(zoomLevel));
            PDFPoint centerpoint = new PDFPoint(XYPoints[0].X, XYPoints[0].Y);
            return centerpoint;
        }


        internal static Point[] ConvertLatLonToXYList(List<Point> modifiedPoints, BoundingBoxDTO boundingBoxLatLon, Rect borderRectXY, int roundDecimalPoints)
        {
            double xScaledMin = borderRectXY.X;
            double yScaledMin = borderRectXY.Y;
            double xScaledMax = borderRectXY.X + borderRectXY.Width;
            double yScaledMax = borderRectXY.Y + borderRectXY.Height;

            double lonMin = boundingBoxLatLon.northWest.longitude;
            double lonMax = boundingBoxLatLon.southEast.longitude;
            double latMin = boundingBoxLatLon.southEast.latitude;
            double latMax = boundingBoxLatLon.northWest.latitude;

            double scale = (float)(xScaledMax - xScaledMin) / (float)(lonMax - lonMin);
            double offsetX = lonMin * scale - xScaledMin;
            double offsetY = latMin * scale - yScaledMin;            // use the same scale to adjust lattitude

            Point[] XYPoints = new Point[modifiedPoints.Count];
            int index = 0;
            foreach (var z in modifiedPoints)
            {
                Point newPoint = new Point();
                newPoint.X = z.X * scale - offsetX;
                newPoint.Y = z.Y * scale - offsetY;

                // The y value is inverted on the map, because lat/lon origin (for florida) is at bottom right
                // x/y origin (for PDF document canvas) is at top left
                // this means for y values number comes out inverted and we have to flip it
                newPoint.Y = yScaledMax * ((yScaledMax - newPoint.Y) / yScaledMax) + yScaledMin;
                XYPoints[index] = newPoint;
                index++;
            }

            return XYPoints;
        }

        internal void ShiftXYPointsToOrigin(Point[] points, Rect border)
        {
            for (int index = 0; index < points.Length; index++)
            {
                points[index].X += border.X;
                points[index].Y += border.Y;

            }
        }




        internal MemoryStream ToPDF()
        {
            MemoryStream pdfStream = new MemoryStream();
            Save(pdfStream);
            pdfStream.Seek(0, SeekOrigin.Begin);
            return pdfStream;
        }


        // increase font size of all lines, and see if they all fit in the space provided.  If they don't, reset the lines to fit
        public static double SetStringListFontSizeToFit(List<PDFString> stringList, double boundaryWidth, double boundaryHeight, double padding)
        {
            // loop, increasing the size of the text until it doesn't fit in the box anymore
            double textFontSize = 1;
            bool increaseTextSize = true;
            bool adjustPadding = false;
            while (increaseTextSize)
            {
                ++textFontSize;
                double TotalRequiredHeight = padding;
                double totalRequiredWidth = padding;
                foreach (var pdfString in stringList)
                {
                    pdfString.UpdateFontSize(textFontSize);

                    TotalRequiredHeight += pdfString.textBoxSize.Height + padding;
                    if (padding + pdfString.textBoxSize.Width + padding > totalRequiredWidth)
                    {
                        totalRequiredWidth = padding + pdfString.textBoxSize.Width + padding;
                    }
                }

                if (totalRequiredWidth >= boundaryWidth)
                {
                    // limited by width, need to recalculate height spacing
                    increaseTextSize = false;
                    adjustPadding = true;
                }
                else if (TotalRequiredHeight >= boundaryHeight)
                {
                    // limited by height, height spacing is fine
                    increaseTextSize = false;
                }
            }
            textFontSize--;

            double totalTextHeight = 0.0;
            foreach (var pdfString in stringList)
            {
                pdfString.UpdateFontSize(textFontSize);
                totalTextHeight += pdfString.textBoxSize.Height;
            }

            if (adjustPadding)
            {
                padding = (boundaryHeight - totalTextHeight) / (stringList.Count + 1);
            }

            return padding;
        }

    }
}
